package openglsuperbible.Chapter3;

import java.nio.FloatBuffer;
import openglsuperbible.glutils.*;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import org.lwjgl.input.Keyboard;
import static org.lwjgl.input.Keyboard.*;



/**
 * Blending example with improvements.
 * @author Matthew Nicholls
 */
public class Blending {
    int width,height;
    private static final int MATRIX_SIZE = 16;
    private MatrixStack modelViewMatrix;
    private FloatBuffer buff = BufferUtils.createFloatBuffer(MATRIX_SIZE);
    private GLShader flatshader;
    
    private GLBatch squareBatch;
    private GLBatch colorBatch;

    int delta;
    /** time at last frame */
    long lastFrame;
    private float xPos = 0.0f;
    private float yPos = 0.0f;

    private float stepSize = 0.0025f;
    
    private float blockSize = 0.2f;
    private float blockSize2 = 0.25f;
    private float vVerts[] = { -blockSize, -blockSize, 0.0f,1, 
                                blockSize, -blockSize, 0.0f,1,
                                blockSize,  blockSize, 0.0f,1,
                               -blockSize,  blockSize, 0.0f,1};
    private short[] index = {0,1,2,3};
    
    
    private float[] vRed = { 1.0f, 0.0f, 0.0f, 0.5f };
    private float[] vGreen = { 0.0f, 1.0f, 0.0f, 1.0f };
    private float[] vBlue = { 0.0f, 0.0f, 1.0f, 1.0f };
    private float[] vBlack = { 0.0f, 0.0f, 0.0f, 1.0f };
   
    
    
    public Blending(int width, int height) {
        this.width = width;
        this.height = height;       
    }
        
    
    
    public void init()
    {
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f );
        
        flatshader = GLShaderFactory.getFlatShader();         
        modelViewMatrix = new MatrixStack();
        squareBatch = new SimpleGLBatch(GL_TRIANGLE_FAN,vVerts,index);
        
        
        float vBlock[] = { -blockSize2, -blockSize2, 0.0f,1,
                           -blockSize2,  blockSize2, 0.0f,1,
                            blockSize2,  blockSize2, 0.0f,1,
                            blockSize2, -blockSize2, 0.0f,1};
        
        colorBatch = new SimpleGLBatch(GL_TRIANGLE_FAN,vBlock,index);
        blockSize2 = blockSize2 + 0.05f;
        resize();
    }
    
    
    
    
    public void resize()
    {
        glViewport(0, 0, width, height);	     
    }
            
    
    /**
     * Renders four colored boxes using instancing.
     */
    public void render()
    {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        
	flatshader.useShader();  
        
        
        modelViewMatrix.push();
        modelViewMatrix.translate(blockSize*2, blockSize*2, 0);
        modelViewMatrix.fillBuffer(buff);
        flatshader.setUniformMatrix4("mvpMatrix", false, buff);        
        flatshader.setUniform4("vColor", vGreen);        
        colorBatch.draw(flatshader.getAttributeLocations());
        modelViewMatrix.pop(); 
        
        modelViewMatrix.push();
        modelViewMatrix.translate(-blockSize*2, blockSize*2, 0);
        modelViewMatrix.fillBuffer(buff);
        flatshader.setUniformMatrix4("mvpMatrix", false, buff);        
        flatshader.setUniform4("vColor", vRed);        
        colorBatch.draw(flatshader.getAttributeLocations());
        modelViewMatrix.pop(); 
 
        
        modelViewMatrix.push();
        modelViewMatrix.translate(-blockSize*2, -blockSize*2, 0);
        modelViewMatrix.fillBuffer(buff);
        flatshader.setUniformMatrix4("mvpMatrix", false, buff);        
        flatshader.setUniform4("vColor", vBlue);        
        colorBatch.draw(flatshader.getAttributeLocations());
        modelViewMatrix.pop(); 

        
        modelViewMatrix.push();
        modelViewMatrix.translate(blockSize*2, -blockSize*2, 0);
        modelViewMatrix.fillBuffer(buff);
        flatshader.setUniformMatrix4("mvpMatrix", false, buff);        
        flatshader.setUniform4("vColor", vBlack);        
        colorBatch.draw(flatshader.getAttributeLocations());
        modelViewMatrix.pop();
 
        
              
        // The moveing box is rendered last as it is being blended.
        modelViewMatrix.push(); 
        modelViewMatrix.translate(xPos, yPos, 0);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        modelViewMatrix.fillBuffer(buff);
        flatshader.setUniformMatrix4("mvpMatrix", false, buff);
        flatshader.setUniform4("vColor", vRed);
        squareBatch.draw(flatshader.getAttributeLocations()); 
        glDisable(GL_BLEND);
        modelViewMatrix.pop();
    }
    
    /**
     * Move the box with the arrow keys
     * @param delta 
     */
     public void pollInput(float delta) {
		
        if (Keyboard.isKeyDown(KEY_UP)) {
	    yPos += stepSize* delta; 
	}
        
        if (Keyboard.isKeyDown(KEY_DOWN)) {
	    yPos -= stepSize* delta;
	}
        if (Keyboard.isKeyDown(KEY_LEFT)) {
	     xPos -= stepSize* delta;
	}
        if (Keyboard.isKeyDown(KEY_RIGHT)) {
	    xPos += stepSize* delta;
	}
            
	// Collision detection
	if(xPos < (-1.0f + blockSize)) xPos = -1.0f + blockSize;
    
	if(xPos > (1.0f - blockSize)) xPos = 1.0f - blockSize;
	
        if(yPos < (-1.0f + blockSize))  yPos = -1.0f + blockSize;
    
	if(yPos > (1.0f - blockSize)) yPos = 1.0f - blockSize;
                          

    }
    
    	public void start() {
		try {
			Display.setDisplayMode(new DisplayMode(width,height));
                        Display.setTitle("Blending");
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		// init OpenGL here
		init();
		while (!Display.isCloseRequested()) {
			pollInput(getDelta());
			// render OpenGL here
                        render();
			Display.update();
                        Display.sync(60);
                        Display.setVSyncEnabled(true); 
		}
		
		Display.destroy();
	}
	
        
        
        
	public static void main(String[] argv) {
		Blending program = new Blending(800,600);
		program.start();
	}
        
        
        /** 
	 * Calculate how many milliseconds have passed 
	 * since last frame.
	 * 
	 * @return milliseconds passed since last frame 
	 */
	public int getDelta() {
	    long time = getTime();
	    int delta = (int) (time - lastFrame);
	    lastFrame = time;
	 
	    return delta;
	}
	
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	public long getTime() {
	    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
        
}
